//@author A0083782W
#include "WordList.h"

namespace UCTimeHandler {

	AWord* AWord::getNext() const{
		return _next;
	}
	
	AWord* AWord::getPrev() const{
		return _prev;
	}

	void AWord::setNext(AWord* const next) {
		_next = next;
	}
	
	void AWord::setPrev(AWord* const prev) {
		_prev = prev;
	}

	NodeType AWord::getType() const{
		return _myType;
	}
	
	void AWord::setType(const NodeType newType) {
		_myType = newType;
	}
	
	pos_int AWord::getIndex() const{
		return _index;
	}
	
	string AWord::getWord() const{
		return _word;
	}

	Nature AWord::getNature() const{
		return _myNature;
	}

	void AWord::setNature (Nature newNature) {
		_myNature = newNature;
	}

	void AWord::findPartner () {
		if (_myNature == Nature::AMPM ) {
			if (_myType == NodeType::HEAD) {
				_myNature = Nature::USELESS;
				return;
			}
			
			if (_prev->_myNature == Nature::NUMBER) {
				if (atoi (_prev->_word.c_str()) <= MAX_HALF_HOUR) {
					_myNature = Nature::DYING;
					_prev->_word += TIME_PROMOTE_STRING;
					_prev->_word += _word;
					_prev->_myNature = Nature::TIME;
				}
				else {
					_myNature = Nature::USELESS;
				}
			}

			if (_prev->_myNature == Nature::TIME) {
				string temp = _prev->_word.substr (_prev->_word.size()-2,2);
				if (isAM_PM (temp)) {
					_myNature = Nature::USELESS;
					return;
				}
				temp = _prev->_word + _word;
				if (isTime (temp)) {
					_prev->_word = temp;
					_myNature = Nature::USELESS;
					_prev->_myNature = Nature::TIME;
				}
				else {
					_myNature = Nature::USELESS;
				}
			} 
		}
	}

	void AWord::meditate() {
		
		if (isDate (_word,DATE_SEP_CHAR, DATE_SEP_CHAR_N)) {
			_myNature = Nature::DATE;
			findAndReplace (_word, DATE_SEP_CHAR[1], DATE_SEP_CHAR[2]);
		}
		else {
			killChar (_word, '.');
			ignoreCase ( &_word);

			if (isNullString (_word)) {
				_myNature = Nature::DYING;
			}
			else if (isTime(_word)) {
				_myNature = Nature::TIME;
			}
			else if (isNumString(_word)) {
				_myNature = Nature::NUMBER;
			}
			else if (isWeekday (_word)) {
				_myNature = Nature::WEEKDAY;
			}
			else if (isMonth (_word)) {
				_myNature = Nature::MONTHS;
			}
			else if (isAM_PM (_word)) {
				_myNature = Nature::AMPM;
			}
			else if (isMorning (_word)) {
				_myNature = Nature::MORNING;
			}
			else if (isAfternoon (_word)) {
				_myNature = Nature::AFTERNOON;
			}
			else if (isToday (_word)) {
				_myNature = Nature::TODAY;
			}
			else if (isTmr (_word)) {
				_myNature = Nature::TOMORROW;
			}
			else if (isPrep (_word)) {
				_myNature = Nature::PREP;
			}
			else if (isOClock (_word)) {
				_myNature = Nature::OCLOCK;
			}
			else if(isWordTo (_word)) {
				_myNature = Nature::WORD_TO;
			}
			else if( isTimeDuration(_word)) {
				_myNature = Nature::DURATION_TIME;
			}
			else if (isDateDuration (_word)) {
				_myNature = Nature::DURATION_DATE;
			}
			else if (isDurationChar (_word)) {
				_myNature = Nature::DURATION_CHAR;
			}
			else if (isNext (_word)) {
				_myNature = Nature::WORD_NEXT;
			}
			else if (isThis (_word)) {
				_myNature = Nature::WORD_THIS;
			}
			else if (isComing (_word)) {
				_myNature = Nature::WORD_COMING;
			}
			else if (isEvery (_word)) {
				_myNature = Nature::EVERY;
			}
			else if (isEveryDay (_word)) {
				_myNature = Nature::EVERYDAY;
			}
			else if (isWordWeek (_word)) {
				_myNature = Nature::WORD_WEEK;
			}
			else if (isWordDay (_word)) {
				_myNature = Nature::WORD_DAY;
			}
			else if (isWordMonth (_word)) {
				_myNature = Nature::WORD_MONTHS;
			}
			else if (isWordWeekly (_word)) {
				_myNature = Nature::WORD_WEEKLY;
			}
			else if (isWordMonthly (_word)) {
				_myNature = Nature::WORD_MONTHLY;
			}
			else {
				_myNature = Nature::USELESS;
			}
		}
	}
	
	void AWord::makeOClockFriends() {
		if (_myNature == Nature::NUMBER) {
			
			//check for oclock
			if (_myType != NodeType::TAIL) {
				if (_next->_myNature == Nature::OCLOCK) {
					const pos_int hourValue = atoi (_word.c_str());
					if (hourValue > MAX_HOUR) {
						if (*_throwable == true ) {
							ThrowID newID = ThrowID::WordList_Illegal_Time;
							throw newID;
						}
						_myNature = Nature::USELESS;
						_next->_myNature = Nature::DYING;
					}
					else if (hourValue > MAX_HALF_HOUR && hourValue <= MAX_HOUR) {
						_word += TIME_PROMOTE_STRING;
						_myNature = Nature::TIME;
						_next->_myNature = Nature::DYING;
					}
					else {
						AWord* temp;
						temp = _next;
						while (temp->_myNature != Nature::AFTERNOON && 
							temp->_myType != NodeType::TAIL) {
								temp = temp->_next;
						}
						if (temp->_myNature == Nature::AFTERNOON) {
							_word += TIME_PM_PROMOTE_STRING;
						}
						else {
							_word += TIME_AM_PROMOTE_STRING;
						}
						_myNature = Nature::TIME;
						_next->_myNature = Nature::DYING;
					}
				}
			}
		}
	}

	void AWord::makeForcedFriends () {
		if (_myNature == Nature::NUMBER) {
			if (_myType != NodeType::HEAD) {
				if (_prev->_myNature == Nature::PREP) {
					const pos_int hourValue = atoi (_word.c_str());
					if (hourValue > MAX_HOUR) {
						_prev->_myNature = Nature::USELESS;
					}
					else if (hourValue > MAX_HALF_HOUR && hourValue <= MAX_HOUR) {
						_word += TIME_PROMOTE_STRING;
						_myNature = Nature::TIME;
						_prev->_myNature = Nature::USELESS;
					}
					else {
						AWord* temp;
						temp = (AWord*) this;
						while (temp->_myNature != Nature::AFTERNOON && 
							temp->_myType != NodeType::TAIL) {
								temp = temp->_next;
						}
						if (temp->_myNature == Nature::AFTERNOON) {
							_word += TIME_PM_PROMOTE_STRING;
						}
						else {
							_word += TIME_AM_PROMOTE_STRING;
						}
						_myNature = Nature::TIME;
						_prev->_myNature = Nature::USELESS;
					}
				}
			}
		}
	}
	void AWord::makeTMRFriends() {
		if (_myNature == Nature::TOMORROW) {
			CurrentTime clock;
			Time testTimer;
			testTimer = clock;
			testTimer++;
			_word = testTimer.toDateString();
			_myNature = Nature::DATE;
		}
	}
	void AWord::makeTodayFriends() {
		if (_myNature == Nature::TODAY) {
			CurrentTime clock;
			Time testTimer;
			testTimer = clock;
			_word = testTimer.toDateString();
			_myNature = Nature::DATE;
		}
	}
	void AWord::makeWeekFriends() {
		if (_myNature == Nature::WEEKDAY) {
			if(_myType != NodeType::HEAD) {
				CurrentTime clock;
				Time testTimer;
				WeekDay myDay = getDayOfWeek (_word);

				testTimer = clock;
				if (_prev->_myNature == Nature::WORD_NEXT) {
					goToNextWeek(testTimer);
				}
			
				while ((WeekDay)testTimer.getTime(TimeType::DAYOFWEEK) != myDay) {
					testTimer++;
				}

				_myNature = Nature::DATE;
				_word = testTimer.toDateString();
			}
			else {
				CurrentTime clock;
				Time testTimer;
				WeekDay myDay = getDayOfWeek (_word);
				testTimer = clock;
				while ((WeekDay)testTimer.getTime(TimeType::DAYOFWEEK) != myDay) {
					testTimer++;
				}
				_myNature = Nature::DATE;
				_word = testTimer.toDateString();
			}
		}
	}

	void AWord::makeDurationFriends(string& date1, string& time1, string& date2, string& time2) {
		if (_myNature == Nature::WORD_TO || _myNature == Nature::DURATION_CHAR) {
			if( _myType == NodeType::BODY) {
				Time testTimer1;
				Time testTimer2;
				if (_prev->_myNature == Nature::TIME && _next->_myNature == Nature::TIME ) {
					date1 = NULL_STRING;
					date2 = NULL_STRING;
					time1 = _prev->_word;
					time2 = _next->_word;
					readToTime (time1, testTimer1);
					readToTime (time2, testTimer2);
					if (testTimer1 >= testTimer2) {
						if (*_throwable == true) {
							ThrowID newID = ThrowID::WordList_Illegal_Duration;
							throw newID;
						}
						time1 = NULL_STRING;
						time2 = NULL_STRING;
					}
				}
				else if (_prev->_myNature == Nature::DATE && _next->_myNature == Nature::DATE ) {
					time1 = NULL_STRING;
					time2 = NULL_STRING;
					date1 = _prev->_word;
					date2 = _next->_word;
					readToDate (date1, testTimer1);
					readToDate (date2, testTimer2);
					if (testTimer1 >= testTimer2) {
						if (*_throwable == true) {
							ThrowID newID = ThrowID::WordList_Illegal_Duration;
							throw newID;
						}
						date1 = NULL_STRING;
						date2 = NULL_STRING;
					}
				}
				else if (_prev->_myNature == Nature::DATE && _next->_myNature == Nature::TIME ) {
					if (_prev->_myType != NodeType::HEAD && _next->_myType != NodeType::TAIL) {
						if(_prev->_prev->_myNature == Nature::TIME && _next->_next->_myNature == Nature::DATE) {
							time1 = _prev->_prev->_word;
							time2 = _next->_word;
							date1 = _prev->_word;
							date2 = _next->_next->_word;
							readToTime (time1, testTimer1);
							readToDate (date1, testTimer1);
							readToTime (time2, testTimer2);
							readToDate (date2, testTimer2);
							if (testTimer1 >= testTimer2 ) {
								if (*_throwable == true) {
									ThrowID newID = ThrowID::WordList_Illegal_Duration;
									throw newID;
								}
								time1 = NULL_STRING;
								time2 = NULL_STRING;
								date1 = NULL_STRING;
								date2 = NULL_STRING;
							}
						}
					}
				}
				else if (_prev->_myNature == Nature::TIME && _next->_myNature == Nature::DATE ) {
					if (_prev->_myType != NodeType::HEAD && _next->_myType != NodeType::TAIL) {
						if(_prev->_prev->_myNature == Nature::DATE && _next->_next->_myNature == Nature::TIME) {
							time1 = _prev->_word;
							time2 = _next->_next->_word;
							date1 = _prev->_prev->_word;
							date2 = _next->_word;
							readToTime (time1, testTimer1);
							readToDate (date1, testTimer1);
							readToTime (time2, testTimer2);
							readToDate (date2, testTimer2);
							if (testTimer1 >= testTimer2 ) {
								if (*_throwable == true) {
									ThrowID newID = ThrowID::WordList_Illegal_Duration;
									throw newID;
								}
								time1 = NULL_STRING;
								time2 = NULL_STRING;
								date1 = NULL_STRING;
								date2 = NULL_STRING;
							}
						}
					}
				}
			}
		}
	}

	void AWord::makeMonthFriends() {
		bool yearPresent = false;
		bool dayPresent = false;
		pos_int nDay = 0;
		pos_int nYear = 0;
		pos_int nMonth = 0;
		AWord* pYear = nullptr;
		AWord* pDay = nullptr;

		if (_myNature == Nature::MONTHS) {
			
			switch (Settings::get().getDateDiplayFormat()) {
			case DateDisplayFormat::DDMMYY:
			case DateDisplayFormat::DDMMYYYY:
				if (_myType != NodeType::HEAD) {
					if ( _prev->_myNature == Nature::NUMBER) {
						nDay = atoi(_prev->_word.c_str());
						pDay = _prev;
						dayPresent = true;
					}
				}
				if (_myType != NodeType::TAIL) {
					if ( _next->_myNature == Nature::NUMBER) {
						nYear = atoi(_next->_word.c_str());
						if (nYear >= MAX_YEAR ) {
							nYear -= FOUR_DIGIT_YEAR;
						}
						if (nYear > MAX_YEAR) {
							yearPresent = false;
						}
						else {
							yearPresent = true;
							pYear = _next;
						}
					}
				}
				break;
			case DateDisplayFormat::MMDDYY:
			case DateDisplayFormat::MMDDYYYY:
				if (_myType != NodeType::TAIL) {
					if (_next->_myNature == Nature::NUMBER ) {
						nDay = atoi (_next->_word.c_str());
						dayPresent = true;
						pDay = _next;
					}
					if (_next->_myType != NodeType::TAIL && _next->_myNature == Nature::NUMBER) {
						nYear = atoi (_next->_next->_word.c_str());
						if (nYear >= MAX_YEAR ) {
							nYear -= FOUR_DIGIT_YEAR;
						}
						if (nYear > MAX_YEAR) {
							yearPresent = false;
						}
						else {
							yearPresent = true;
							pYear = _next->_next;
						}
					}
				}
				break;
			case DateDisplayFormat::YYMMDD:
			case DateDisplayFormat::YYYYMMDD:
				if (_myType != NodeType::HEAD ) {
					if (_prev->_myNature == Nature::NUMBER) {
						nYear = atoi (_prev->_word.c_str());
						if (nYear >= MAX_YEAR ) {
							nYear -= FOUR_DIGIT_YEAR;
						}
						if (nYear > MAX_YEAR) {
							yearPresent = false;
						}
						else {
							yearPresent = true;
							pYear = _prev;
						}
					}
				}
				if (_myType != NodeType::TAIL ) {
					if (_next->_myNature == Nature::NUMBER ) {
						nDay = atoi (_prev->_word.c_str());
						pDay = _next;
					}
				}
				break;
			default:
				return;
			}

			nMonth = (pos_int)getMonth(_word);
			Time testTimer;
			if (yearPresent == false) {
				CurrentTime myClock;
				nYear = myClock.getCurrentTime(TimeType::YEAR) - FOUR_DIGIT_YEAR;
			}
			if (dayPresent == true) {
				if (testTimer.setTime (TimeType::YEAR, nYear) == false) {
					return;
				}
				testTimer.setTime (TimeType::MONTH, nMonth);
				if (testTimer.setTime (TimeType::DAY, nDay) == false) {
					return;
				}
				if (testTimer.isLastInputLegal() == false) {
					return;
				}
				if (yearPresent) {
					pYear->_myNature = Nature::DYING;
				}
				pDay->_myNature = Nature::DYING;
				_myNature = Nature::DATE;
				_word = testTimer.toDateString();
			}
		}
	}

	AWord& AWord::operator^ (const int arrayIndex) const {
		const pos_int nSteps = arrayIndex > 0 ? (pos_int)arrayIndex : (pos_int)(-arrayIndex);
		AWord *temp = (AWord*)this;
		if (arrayIndex > 0) {
			for (pos_int i = 1; i <= nSteps; i++) {
				temp = temp->getNext();
			}
		}
		else if (arrayIndex < 0) {
			for (pos_int i = 1; i <= nSteps; i++) {
				temp = temp->getPrev();
			}
		}
		return (AWord&)(*temp);
	}

	AWord::AWord(con_str& Word, const pos_int wordIndex, const bool * throwable) {
		_prev = nullptr;
		_next = nullptr;
		_word = Word;
		_index = wordIndex;
		_throwable = throwable;
		meditate();
	}

	pos_int WordList::getSize() const {
		return _nNum;
	}
	
	void WordList::delNode( AWord* node) {
		if (_nNum == 0) {
			return;
		}
		else if (_nNum == 1) {
			delete _head;
			_head = nullptr;
		}
		else if (_nNum == 2) {
			if (node->getType() == NodeType::HEAD) {
				delete _head;
				_head = _tail;
				_head->setPrev (nullptr);
				_head->setType (NodeType::HEAD);
				_tail = nullptr;
			}
			else if (node->getType() == NodeType::TAIL) {
				delete _tail;
				_head->setNext (nullptr);
			}
			else {
				throw 1;
			}
		}
		else {
			if (node->getType() == NodeType::HEAD) {
				_head = _head->getNext();
				delete node;
				_head->setPrev (nullptr);
				_head->setType (NodeType::HEAD);
			}
			else if (node->getType() == NodeType::BODY) {
				node->getPrev()->setNext (node->getNext());
				node->getNext()->setPrev (node->getPrev());
				delete node;
			}
			else if (node->getType() == NodeType::TAIL) {
				_tail = _tail->getPrev();
				delete node;
				_tail->setNext (nullptr);
				_tail->setType (NodeType::TAIL);
			}
			else {
					
			}
		}
		_nNum --;
	}

	void WordList::solveTime() {
		try {
			currentStack.clear();
			callChildren (Nature::TIME);
			if (currentStack.size() == 0) {
				negotiate (Nature::OCLOCK);
				callChildren (Nature::TIME);
				if (currentStack.size() == 0) {
					negotiate (Nature::NUMBER);
					callChildren (Nature::TIME);
				}
			}
		
			if (currentStack.size() > 0){
				_timeString[0] = currentStack[currentStack.size()-1]->getWord();
			}
			else {
				_timeString[0] = NULL_STRING;
			}
			currentStack.clear();
		}
		catch (ThrowID) {
			throw;
		}
	}

	void WordList::solveDirectDuration() {
		try {
			vector<string> numList;
			vector<pos_int> indicesList;

			currentStack.clear();
			currentStack.shrink_to_fit();
			callChildren (Nature::DURATION_TIME);
			if (currentStack.size() != 0) {
				_isTimeDuration = true;
				splitString (
					currentStack[currentStack.size()-1]->getWord(),
					DURATION_CHAR,
					DURATION_CHAR_N,
					numList,
					indicesList
					);
				_timeString[0] = numList[0];
				_timeString[1] = numList[1];
			}
			else {
				callChildren (Nature::DURATION_DATE);
				if (currentStack.size() != 0) {
					_isDateDuration = true;
					splitString (
						currentStack[currentStack.size()-1]->getWord(),
						DURATION_CHAR,
						DURATION_CHAR_N,
						numList,
						indicesList
						);
					_dateString[0] = numList[0];
					_dateString[1] = numList[1];
				}
			}
		}
		catch (ThrowID) {
			throw;
		}
	}

	void WordList::removeDying() {
		currentStack.clear();
		callChildren (Nature::DYING);
		for (auto i: currentStack) {
			delNode (i);
		}
	}

	void WordList::solveComplexDuration() {
		try {
			negotiate (Nature::OCLOCK);
			negotiate (Nature::NUMBER);
			negotiate (Nature::MONTHS);
			negotiate (Nature::WEEKDAY);
			negotiate (Nature::TOMORROW);
			negotiate (Nature::TODAY);
			removeDying();
		
			callChildren (Nature::WORD_TO);
			callChildren (Nature::DURATION_CHAR);
			if (currentStack.size() == 0) {
				return;
			}

			string date1, time1, date2, time2 ;
			for (pos_int i = currentStack.size() - 1; i >=0; i--) {
				currentStack[i]->makeDurationFriends(date1, time1, date2, time2);
				break;
			}

			if (date1 != NULL_STRING && date2 != NULL_STRING  &&
				time1 == NULL_STRING && time2 == NULL_STRING
				) {
				//Date duration
				_isDateDuration = true;
				_dateString[0] = date1;
				_dateString[1] = date2;
			}
			else if (time1 != NULL_STRING && time2 != NULL_STRING &&
				date1 == NULL_STRING && date2 == NULL_STRING 
				) {
				//Time duration
				_isTimeDuration = true;
				_timeString[0] = time1;
				_timeString[1] = time2;
			}
			else if (date1 != NULL_STRING && date2 != NULL_STRING && 
				time1 != NULL_STRING && time2 != NULL_STRING 
				) {
				// Both date & time Duration
				_isTimeDuration = true;
				_isDateDuration = true;
				_timeString[0] = time1;
				_timeString[1] = time2;
				_dateString[0] = date1;
				_dateString[1] = date2;
			}
		}
		catch (ThrowID) {
			throw;
		}
	}

	AWord* WordList::getHead() {
		return _head;
	}

	void WordList::solveRepeat() {
		try {
			currentStack.clear();
			currentStack.shrink_to_fit();
			_repeatRule.frequency = RepeatFrequency::NO_REPEAT;
			_repeatRule.startOfWeek = WeekDay::UNCLEAR;
			_repeatRule.period = 0;
			callChildren (Nature::EVERYDAY);
			if (currentStack.size() != 0) {
				_repeatRule.frequency = RepeatFrequency::EVERY_N_DAY;
				_repeatRule.period = 1;
				return;
			}
			callChildren (Nature::WORD_WEEKLY);
			if (currentStack.size() != 0) {
				_repeatRule.frequency = RepeatFrequency::EVERY_N_DAY;
				_repeatRule.period = 7;
				return;
			}
			callChildren (Nature::WORD_MONTHLY);
			if (currentStack.size() != 0) {
				_repeatRule.frequency = RepeatFrequency::EVERY_MONTH;
				return;
			}
			else {
				callChildren (Nature::EVERY);
				if (currentStack.size() != 0) {
					AWord* temp;
					temp = currentStack[currentStack.size()-1];
					if (temp->getType() != NodeType::TAIL &&
						_nNum > 1) {
							temp = temp->getNext();
							switch (temp->getNature()) {
							case Nature::WEEKDAY:
								_repeatRule.startOfWeek = getDayOfWeek(temp->getWord());
								_repeatRule.frequency = RepeatFrequency::EVERY_N_DAY;
								_repeatRule.period = MAX_WEEK;
								break;
							case Nature::WORD_DAY:
								_repeatRule.frequency = RepeatFrequency::EVERY_N_DAY;
								_repeatRule.period= 1;
								break;
							case Nature::WORD_WEEK:
								_repeatRule.frequency = RepeatFrequency::EVERY_N_DAY;
								_repeatRule.period = MAX_WEEK;
								break;
							case Nature::WORD_MONTHS:
								_repeatRule.frequency = RepeatFrequency::EVERY_MONTH;
								break;
							case Nature::NUMBER:
								if (temp->getType() != NodeType::TAIL) {
									if (temp->getNext()->getNature() == Nature::WORD_DAY) {
										_repeatRule.frequency = RepeatFrequency::EVERY_N_DAY;
										_repeatRule.period = atoi(temp->getWord().c_str());
									}
									else {
										goto ReportErrorInSolveRepeat;
									}
								}
								else {
									goto ReportErrorInSolveRepeat;
								}
								break;
ReportErrorInSolveRepeat:
							default:
								if (_throwable == true ) {
									ThrowID newID = ThrowID::WordList_Illegal_Repeat;
									throw newID;
								}
								break;
							}
					}
				}
			}
		}
		catch (ThrowID) {
			throw;
		}
	}

	WordList::WordList() {
		_nNum = 0;
		_timeString[0] = NULL_STRING;
		_timeString[1] = NULL_STRING;
		_dateString[0] = NULL_STRING;
		_dateString[1] = NULL_STRING;
		_isDateDuration = false;
		_isTimeDuration = false;
		_throwable = false;
		_head = nullptr;
		_tail = nullptr;
		messageString = NULL_STRING;
		_repeatRule.frequency = RepeatFrequency::NO_REPEAT;
		_repeatRule.period = 0;
		_repeatRule.startOfWeek = WeekDay::UNCLEAR;
	}

	REPEAT_RULE WordList::getRepeatRule() {
		return _repeatRule;
	}

	void WordList::solveDate() {
		try {
			currentStack.clear();
			callChildren (Nature::DATE);
			if (currentStack.size() == 0) {
				negotiate (Nature::MONTHS);
				callChildren (Nature::DATE);
				if (currentStack.size() == 0) {
					negotiate (Nature::WEEKDAY);
					callChildren (Nature::DATE);
					if (currentStack.size() == 0) {
						negotiate (Nature::TOMORROW);
						callChildren (Nature::DATE);
						if (currentStack.size() == 0) {
							negotiate (Nature::TODAY);
							callChildren (Nature::DATE);
						}
					}
				}
			}
		
			if (currentStack.size() > 0){
				_dateString[0] = currentStack[currentStack.size()-1]->getWord();
			}
			else {
				_dateString[0] = NULL_STRING;
			}
			currentStack.clear();
		}
		catch (ThrowID) {
			throw;
		}
	}

	void WordList::solveAll() {
		//pick out durations first;
		try {
			if (_nNum == 0) {
				return;
			}
			solveDirectDuration();
			solveRepeat();
			//solve Date;
			if(_isDateDuration == false) {
				solveDate();
			}
			//Solve Time;
			if (_isTimeDuration == false) {
				solveTime();
			}

			if ((_repeatRule.frequency == RepeatFrequency::EVERY_N_DAY ||
				_repeatRule.frequency == RepeatFrequency::EVERY_MONTH) &&
				_repeatRule.startOfWeek == WeekDay::UNCLEAR && 
				_dateString[0] == NULL_STRING) {
					CurrentTime aClock;
					Time newTimer;
					newTimer = aClock;
					_dateString[0] = newTimer.toDateString();
			}
			solveComplexDuration();
		}
		catch (ThrowID) {
			throw;
		}
	}

	bool WordList::isTimeStringDuration() const {
		return _isTimeDuration;
	}
	
	bool WordList::isDateStringDuration() const {
		return _isDateDuration;
	}
	
	flexiReturnPtr WordList::getTimeString() const {
		if (_isTimeDuration) {
			return (flexiReturnPtr) (&_timeString);
		}
		else {
			return (flexiReturnPtr)(&_timeString[0]);
		}
	}

	void WordList::setThrowable(const bool newValue) {
		_throwable = newValue;
	}

	flexiReturnPtr WordList::getDateString() const {
		if (_isTimeDuration) {
			return (flexiReturnPtr*)(&_dateString);	
		}
		else {
			return (flexiReturnPtr*)(&_dateString[0]);
		}
	}

	void WordList::setMessage(con_str& mainString) {
		vector<string> list;
		vector<pos_int> indices;

		reset();

		messageString = mainString;
		_nNum = splitString (mainString, SEP_CHAR_LIST, SEP_CHAR_LIST_N ,list, indices);

		if (_nNum == 0 ) {
			return;
		}
		for (pos_int i = 0; i < _nNum; i++) {
			if (i == 0) {
				_head = new AWord (list[i], indices[i], &this->_throwable);
				_head->setType (NodeType::HEAD);
			}
			else if (i == 1) {
				_tail = new AWord (list[i], indices[i], &this->_throwable);
				_tail->setType (NodeType::TAIL);
				_head->setNext (_tail);
				_tail->setPrev (_head);
			}
			else {
				AWord* temp;
				temp = _tail;
				temp->setType (NodeType::BODY);
				_tail = new AWord (list[i], indices[i], &this->_throwable);
				_tail->setType (NodeType::TAIL);
				temp->setNext (_tail);
				_tail->setPrev (temp);
			}
		}

		currentStack.clear();
		currentStack.shrink_to_fit();
		callChildren (Nature::AMPM);
		for (pos_int i = 0; i < currentStack.size(); i++ ) {
			currentStack[i]->findPartner();
		}
	}

	void WordList::reset() {
		while (_nNum > 0) {
			if (_nNum == 1) {
				delete _head;
			}
			else {
				AWord* temp;
				temp= _tail;
				_tail = _tail->getPrev();
				delete temp;
			}
			_nNum--;
		}

		currentStack.clear();

		_timeString[0] = NULL_STRING;
		_timeString[1] = NULL_STRING;
		_dateString[0] = NULL_STRING;
		_dateString[1] = NULL_STRING;
		_isDateDuration = false;
		_isTimeDuration = false;
		_head = nullptr;
		_tail = nullptr;
		messageString = NULL_STRING;
		_repeatRule.reset ();
	}

	void WordList::callChildren(Nature specificNature) {
		AWord *temp = _head;

		if (_nNum == 0) {
			return ;
		}
		else if (_nNum == 1) {
			if (temp->getNature() == specificNature ) {
				currentStack.push_back (temp);
			}
		}
		else {
			while (temp->getType() != NodeType::TAIL) {
				if (temp->getNature() == specificNature ) {
					currentStack.push_back (temp);
				}
				temp = temp->getNext();
			}

			if (temp->getNature() == specificNature ) {
				currentStack.push_back (temp);
			}
		}
	}

	void WordList::negotiate(Nature calledNature) {
		try {
			currentStack.clear();
			currentStack.shrink_to_fit();
			switch (calledNature) {
			case Nature::OCLOCK:	
				callChildren (Nature::NUMBER);
				for (pos_int i = 0; i < currentStack.size(); i++) {
					currentStack[i]->makeOClockFriends();
				}
				break;
			case Nature::NUMBER:
				callChildren (Nature::NUMBER);
				for (pos_int i = 0; i < currentStack.size(); i++) {
					currentStack[i]->makeForcedFriends();
				}
				break;
		
			case Nature::MONTHS:
				callChildren (Nature::MONTHS);
				for (pos_int i = 0; i < currentStack.size(); i++ ) {
					currentStack[i]->makeMonthFriends();
				}
				break;
			case Nature::WEEKDAY:
				callChildren (Nature::WEEKDAY);
				for (pos_int i = 0; i < currentStack.size(); i++) {
					currentStack[i]->makeWeekFriends();
				}
				break;
			case Nature::TOMORROW:
				callChildren (Nature::TOMORROW);
				for (pos_int i = 0; i < currentStack.size(); i++) {
					currentStack[i]->makeTMRFriends();
				}
				break;
			case Nature::TODAY:
				callChildren (Nature::TODAY);
				for (pos_int i = 0; i < currentStack.size(); i++) {
					currentStack[i]->makeTodayFriends();
				}
				break;
			}
			currentStack.clear();
			currentStack.shrink_to_fit();
		}
		catch (ThrowID) {
			throw;
		}
	}
	
	WordList::WordList(con_str& mainString) {
		_nNum = 0;
		setMessage (mainString);
		_timeString[0] = NULL_STRING;
		_timeString[1] = NULL_STRING;
		_dateString[0] = NULL_STRING;
		_dateString[1] = NULL_STRING;
		_throwable = false;
		_isDateDuration = false;
		_isTimeDuration = false;
	}

	WordList::~WordList() {
		reset();
	}

	void WordList::getStartEndTimer (Time& startTime, Time& endTime) {
		if (!isNullString(_timeString[0])) {
			readToTime (_timeString[0], startTime);
		}
		if (!isNullString(_dateString[0])) {
			readToDate (_dateString[0], startTime);
		}
		if (isDateStringDuration() == false && isTimeStringDuration() == true) {
			readToDate (_dateString[0], endTime);
			readToTime (_timeString[1],endTime);
		}
		else if  (isDateStringDuration() == true && isTimeStringDuration() == false) {
			readToTime (_timeString[0], endTime);
			readToDate (_dateString[1], endTime);
		}
		else if  (isDateStringDuration() == true && isTimeStringDuration() == true) {
			readToTime (_timeString[1], endTime);
			readToDate (_dateString[1], endTime);
		}
	}

	AWord& WordList::operator[] (const pos_int postion) const {
		AWord *temp = _head;
		for (pos_int i = 0; i < postion; i++) {
			temp =  temp->getNext();
		}
		return *temp;
	}
	
	bool isWeekday (con_str& mainString) {
		if (isNullString (mainString)) {
			return false;
		}
		for (pos_int i = 0; i < WEEKDAY_LIST_N; i++) {
			if (isEqualString (mainString, WEEKDAY_LIST[i], CONST_ARRAY_MAX)) {
				return true;
			}
		}
		return false;
	}
	
	bool isMonth (con_str& mainString) {
		if (isNullString (mainString)) {
			return false;
		}
		for (pos_int i = 1; i < MONTH_LIST_N; i++) {
			if (isEqualString (mainString, MONTH_LIST[i], CONST_ARRAY_MAX)) {
				return true;
			}
		}
		return false;
	}

	bool isTime (con_str& mainString) {
		if (mainString.size() < 3) {
			return false;
		}

		string testString = mainString;
		bool hasSuffix = false;
		killChar (testString, '.');

		string suffix  = testString.substr (testString.size() - 2, 2);
		for (pos_int i = 0; i < AM_PM_N; i++) {
			if(isEqualString(suffix, AM_PM[i],CONST_ARRAY_MAX)) {
				hasSuffix = true;
				break;
			}
		}

		if (hasSuffix) {
			testString = testString.substr (0, testString.size()-2);
		}
		
		if (hasSuffix && isNumString (testString) && 
			(atoi(testString.c_str()) <= MAX_HALF_HOUR)) {
				return true;
		}

		if (hasSuffix) {
			return isStandardTime (testString, true);
		}
		else {
			return isStandardTime (testString, false);
		}

	}

	bool isStandardTime (con_str& mainString, bool isAMPM) {
		if (mainString.size() != TIME_STRING_LENGTH_4 && 
			mainString.size() != TIME_STRING_LENGTH_5) {
			return false;
		}

		pos_int colonPos = 0;
		for (; colonPos < mainString.size(); colonPos++) {
			if (mainString[colonPos] == ':') {
				break;
			}
		}

		if (colonPos != COLON_POS_1 && colonPos!= COLON_POS_2) {
			return false;
		}

		const string leftPart = mainString.substr (0, colonPos );
		const string rightPart = mainString.substr (colonPos+1, mainString.size());

		if (isNumString (leftPart) ==false || isNumString(rightPart) == false ||
			rightPart.size() != MINUTES_STRING_SIZE) {
			return false;
		}

		if (atoi(rightPart.c_str()) > MAX_MINUTE) {
			return false;
		}

		if (isAMPM) {
			if (atoi(leftPart.c_str()) > MAX_HALF_HOUR) {
				return false;
			}
		}
		else {
			if (atoi(leftPart.c_str()) > MAX_HOUR) {
				return false;
			}
		}

		return true;
	}

	bool isDate (con_str& mainString, con_chr dateSepChar[], const pos_int dateSepCharSize) {
		vector<string> numList ;
		vector<pos_int> numIndices;
		const pos_int nPart = splitString (mainString, dateSepChar, dateSepCharSize, numList, numIndices);
		string* pDay;
		string* pMonth;
		string* pYear = nullptr;
		
		if (nPart != MIN_NUM_YEAR_PART && nPart != MAX_NUM_YEAR_PART) {
			return false;
		}
		
		for (auto T:numList ) {
			if (isNumString (T) == false) {
				return false;
			}
		}
		
		ReadSettingPtr config = & Settings::get(); 
		switch (config->getDateDiplayFormat()) {
			case DateDisplayFormat::DDMMYY:
			case DateDisplayFormat::DDMMYYYY:
				pDay = &numList[0];
				pMonth = &numList[1];
				if (nPart == MAX_NUM_YEAR_PART) {
					pYear = & numList[2];
				}
				break;
			case DateDisplayFormat::MMDDYY:
			case DateDisplayFormat::MMDDYYYY:
				pMonth = &numList[0];
				pDay = &numList[1];
				if (nPart == MAX_NUM_YEAR_PART) {
					pYear = & numList[2];
				}
				break;
			case DateDisplayFormat::YYMMDD:
			case DateDisplayFormat::YYYYMMDD:
				if (nPart == MAX_NUM_YEAR_PART) {
					pYear = & numList[0];
					pDay = &numList[1];
					pMonth = &numList[2];
				}
				else {
					pDay = &numList[0];
					pMonth = &numList[1];
				}
				break;
			default:
				return false;
				break;
		}
		const pos_int nDay = atoi (pDay->c_str());
		const pos_int nMonth = atoi (pMonth->c_str());
		Time testTimer;

		bool timerFlag;
		timerFlag = testTimer.setTime (TimeType::DAY, nDay);
		if (!timerFlag) {
			return false;
		}
		if (!testTimer.isLastInputLegal()) {
			return false;
		}

		timerFlag = testTimer.setTime (TimeType::MONTH, nMonth);
		if (!timerFlag) {
			return false;
		}
		if (!testTimer.isLastInputLegal()) {
			return false;
		}

		if (nPart == MAX_NUM_YEAR_PART ) {
			pos_int nYear = atoi(pYear->c_str());
			if (nYear >= FOUR_DIGIT_YEAR ) {
				nYear -= FOUR_DIGIT_YEAR;
			}
			timerFlag = testTimer.setTime (TimeType::YEAR, nYear);
			if (!timerFlag) {
				return false;
			}
			if (!testTimer.isLastInputLegal()) {
				return false;
			}
		}
		return true;
	}

	bool isAM_PM (con_str& mainString) {
		for (pos_int i = 0; i < AM_PM_N; i++) {
			if( isEqualString(mainString, AM_PM[i], CONST_ARRAY_MAX)) {
				return true;
			}
		}
		return false;
	}

	bool isMorning (con_str& mainString) {
		if( isEqualString(mainString, MORNING_STRING, MORNING_STRING_N)) {
				return true;
		}
		else {
			return false;
		}
	}

	bool isAfternoon (con_str& mainString) {
		if (isEqualString (mainString, AFTERNOON_STRING, AFTERNOON_STRING_N)) {
			return true;
		}
		else {
			return false;
		}
	}
	
	bool isToday (con_str& mainString) {
		if (mainString == TODAY) {
			return true;
		}
		else {
			return false;
		}
	}

	bool isTmr (con_str& mainString) {
		if (isEqualString (mainString, TOMORROW, TOMORROW_N)) {
			return true;
		}
		else {
			return false;
		}
	}

	bool isPrep (con_str& mainString) {
		if (isEqualString (mainString, PREP, PREP_N)) {
			return true;
		}
		else {
			return false;
		}
	}
	
	bool isOClock (con_str& mainString) {
		if (isEqualString (mainString, OCLOCK, OCLOCK_N)) {
			return true;
		}
		else {
			return false;
		}
	}

	bool isWordTo (con_str& mainString) {
		if (mainString == WORD_TO ) {
			return true;
		}
		else {
			return false;
		}
	}

	bool isTimeDuration (con_str& mainString) {
		vector<string> timeList;
		vector<pos_int> timeIndices;
		const pos_int nPart = splitString (
			mainString,
			DURATION_CHAR,
			DURATION_CHAR_N,
			timeList,
			timeIndices
		);

		if (nPart != DURATION_PARTS) {
			return false;
		}
		if (isTime (timeList[0]) && isTime (timeList[1])) {
			Time testTimer1;
			Time testTimer2;
			readToTime (timeList[0], testTimer1);
			readToTime (timeList[1], testTimer2);
			if (testTimer2 > testTimer1) {
				return true;
			}
		}
		return false;
	}

	bool isDateDuration (con_str& mainString) {
		vector<string> dateList;
		vector<pos_int> dateIndices;
		const pos_int nPart = splitString (
			mainString,
			DURATION_CHAR,
			DURATION_CHAR_N,
			dateList,
			dateIndices
		);

		if (nPart != DURATION_PARTS) {
			return false;
		}
		if (isDate (dateList[0], DATE_SEP_CHAR, DATE_SEP_CHAR_N) && isDate (dateList[1], DATE_SEP_CHAR, DATE_SEP_CHAR_N)) {
			Time testTimer1;
			Time testTimer2;
			readToDate (dateList[0], testTimer1);
			readToDate (dateList[1], testTimer2);
			if (testTimer2 > testTimer1) {
				return true;
			}
		}
		return false;
	}

	bool isDurationChar (con_str& mainString) {
		if (mainString.size() == 1) {
			return isEqualChar(mainString[0], DURATION_CHAR, DURATION_CHAR_N);
		}
		return false;
	}

	bool isNext (con_str& mainString) {
		if (mainString == STRING_NEXT) {
			return true;
		}
		else {
			return false;
		}
	}

	bool isThis (con_str& mainString) {
		if (mainString == STRING_THIS) {
			return true;
		}
		else {
			return false;
		}
	}

	bool isComing (con_str& mainString) {
		if (mainString == STRING_COMING) {
			return true;
		}
		else {
			return false;
		}
	}

	bool isEvery (con_str& mainString) {
		if (isEqualString(mainString ,STRING_EVERY, STRING_EVERY_N)) {
			return true;
		}
		else {
			return false;
		}
	}

	bool isEveryDay (con_str& mainString) {
		if ( isEqualString(mainString, STRING_EVERYDAY, STRING_EVERYDAY_N)) {
			return true;
		}
		else {
			return false;
		}
	}

	bool isWordWeek (con_str& mainString) {
		if (isEqualString (mainString, STRING_WEEK, STRING_WEEK_N)) {
			return true;
		}
		else {
			return false;
		}
	}

	bool isWordDay (con_str& mainString) {
		if (isEqualString (mainString, STRING_DAY, STRING_DAY_N )) {
			return true;
		}
		else {
			return false;
		}
	}
	
	bool isWordMonth (con_str& mainString) {
		if (isEqualString (mainString, STRING_MONTH, STRING_MONTH_N)) {
			return true;
		}
		else {
			return false;
		}
	}

	Month getMonth (con_str& mainString) {
		for (pos_int i = 0; i < MONTH_LIST_N; i++ ) {
			for (pos_int j = 0; j < CONST_ARRAY_MAX; j++) {
				if (mainString == MONTH_LIST[i][j]) {
					return (Month)i;
				}
			}
		}
		return (Month)0;
	}

	WeekDay getDayOfWeek (con_str& mainString) {
		for (pos_int i = 0; i < WEEKDAY_LIST_N; i++ ) {
			for (pos_int j = 0; j < CONST_ARRAY_MAX; j++ ) {
				if (mainString == WEEKDAY_LIST[i][j]) {
					return (WeekDay)i;
				}
			}
		}
		return WeekDay::UNCLEAR;
	}

	bool readToTime (con_str& mainString, Time& timer) {
		if (isTime(mainString)) {
			bool isPMstr = false;
			string temp = NULL_STRING;
			vector<string> numList;
			vector<pos_int> indicesList;
			con_str suffix = mainString.substr (mainString.size() - 2, 2);
			if (isAM_PM(suffix)) {
				if (suffix == AM_PM[2][0] ) {
					isPMstr = true;
				}
				temp = mainString.substr (0,mainString.size () - 2);
			}
			else {
				temp = mainString;
			}

			splitString (temp, TIME_SEP_CHAR, TIME_SEP_CHAR_N, numList, indicesList);
			if (isPMstr) {
				ByteInt hour = atoi(numList[0].c_str());
				if (hour == 12) {
					timer.setTime (TimeType::HOUR, atoi(numList[0].c_str()));
				}
				else {
					timer.setTime (TimeType::HOUR, atoi(numList[0].c_str()) + MAX_HALF_HOUR);
				}
			}
			else {
				timer.setTime (TimeType::HOUR, atoi(numList[0].c_str()));
			}

			if (numList.size() == 2) {
				timer.setTime (TimeType::MINUTE, atoi (numList[1].c_str()));
			}
			return true;
		}
		else {
			return false;
		}
	}
	
	bool readToDate (con_str& mainString, Time& timer) {
		if (isDate(mainString,DATE_SEP_CHAR, DATE_SEP_CHAR_N)) {
			ReadSettingPtr config = &Settings::get();
			vector<string> numList;
			vector<pos_int> indiceList;
			CurrentTime clock;
			string* pYear = nullptr;
			string* pMonth = nullptr;
			string* pDay = nullptr;
			bool yearPresent = false;

			splitString (mainString, DATE_SEP_CHAR, DATE_SEP_CHAR_N, numList, indiceList);
			if(numList.size() == MIN_NUM_YEAR_PART) {
				timer.setTime (TimeType::YEAR, clock.getCurrentTime(TimeType::YEAR) - FOUR_DIGIT_YEAR);
			}
			else {
				yearPresent = true;
			}

			switch (config->getDateDiplayFormat()) {
			case DateDisplayFormat::DDMMYY:
			case DateDisplayFormat::DDMMYYYY:
				if (yearPresent) {
					pYear = & numList[2];
				}
				pMonth = &numList[1];
				pDay = &numList[0];
				break;
			case DateDisplayFormat::MMDDYY:
			case DateDisplayFormat::MMDDYYYY:
				if (yearPresent) {
					pYear = &numList[2];
				}
				pMonth = &numList[0];
				pDay = &numList[1];
				break;
			case DateDisplayFormat::YYMMDD:
			case DateDisplayFormat::YYYYMMDD:
				if (yearPresent) {
					pYear = &numList[0];
				}
				pMonth = &numList[1];
				pDay = &numList[2];
				break;
			default:
				return false;
			}
			
			if (yearPresent) {
				pos_int temp = atoi((*pYear).c_str());
				if (temp > FOUR_DIGIT_YEAR) {
					temp -= FOUR_DIGIT_YEAR;
				}
				timer.setTime (TimeType::YEAR, temp);
			}

			timer.setTime (TimeType::MONTH, atoi ((*pMonth).c_str ()));
			timer.setTime (TimeType::DAY, atoi ((*pDay).c_str ()));
			return true;
		}
		else {
			return false;
		}
	}

	bool isWordMonthly (con_str& mainString) {
		if (mainString == STRING_MONTHLY) {
			return true;
		}
		else {
			return false;
		}
	}
	
	bool isWordWeekly (con_str& mainString) {
		if (mainString == STRING_WEEKLY) {
			return true;
		}
		else {
			return false;
		}
	}

	void goToNextWeek (Time & timer) {
		if ((WeekDay)timer.getTime(TimeType::DAYOFWEEK) == WeekDay::MONDAY) {
			for (pos_int i = 0; i < MAX_WEEK; i++) {
				timer++;
			}
		}
		else {
			while ((WeekDay)timer.getTime(TimeType::DAYOFWEEK) != WeekDay::MONDAY) {
				timer++;
			}
		}
	}
};